//
//  Request.swift
//  AG
//
//  Created by BirdMichael on 2020/4/27.
//  Copyright © 2020 BirdMichael. All rights reserved.
//

import Foundation
import HandyJSON
import BWToolKit
import Moya

public typealias RequestParams = Request.Params
public typealias RequestHeaders = Request.Headers

extension Request{
    public typealias Headers = [String: String]
    public typealias Params = [String: Any]
    public typealias ResultingData = BlockT<State<Data>>
    public typealias ResultingModel<T> = BlockT<State<T>>
}

extension Request {
    public enum Method: String {
        case post, get, delete, put, patch
        case head, options, trace, connect
        
        public var rawValue: String {
            return "\(self)".uppercased()
        }
    }
}

public class Request {
    public internal(set) var method: Request.Method!
    public internal(set) var host: API.Host!
    public internal(set) var path = ""
    public internal(set) var params: Params = [:]
    public internal(set) var headers: Headers = [:]
    public internal(set) var taskType: TaskType = .plain
    public internal(set) var timeouts: Double = 20
    public internal(set) var cachePolicy: URLRequest.CachePolicy = .reloadIgnoringLocalCacheData
    
    ///TODO
    public enum Status {
        case ready
        case loading
        case cancelled
        case failed(with: Request.Error)
        case finished
        case success
    }
    public internal(set) var status: Status = .ready
    
    public var isCancelled: Bool {
        return cancellToken?.isCancelled ?? false
    }
    internal var cancellToken: Moya.Cancellable?
    public func cancel() {
        guard case .loading = status else {
            return
        }
        cancellToken?.cancel()
    }
    
    private init(){}
    
    public init(
        method: Method = .get,
        taskType: TaskType = .plain,
        headers: Headers = [:],
        host: API.Host,
        path: String = "",
        params: Params = [:],
        timeouts: Double = 20,
        cache policy: URLRequest.CachePolicy = .reloadIgnoringLocalCacheData
    ) {
        self.headers = headers
        self.taskType = taskType
        self.method = method
        self.host = host
        self.path = path
        self.params = params
        self.timeouts = timeouts
        self.cachePolicy = policy
        
        if let pathParams = path.bw_urlSplitedParameters,
            method == .get {
            for (k,v) in pathParams {
                self.params[k] = v
            }
            self.path = path.bw_urlParametersRemoved
            self.taskType = .urlParameters
        }
    }
}

extension Request {
    public var fullUrl: String {
        return host.string + "/" + path
    }
}

extension String {
    public var bw_urlEscaped: String {
        self.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
    }
    
    public var bw_urlParametersRemoved: String {
        guard let barePath = components(separatedBy: "?").first else {
            return self
        }
        return barePath
    }
    
    public var bw_urlSplitedParameters: [String: String]?
    {
        guard let comps = components(separatedBy: "?").last?
                .components(separatedBy: "&") else {
            return nil
        }
        let splits: [[String: String]] = comps.compactMap
        {
            let c = $0.components(separatedBy: "=")
            switch c.count {
            case 1:
                if c.first?.hasSuffix("=") == true {
                    return [c.first!: ""]
                }
            case 2:
                return [c.first!: c.last!]
            default: break
            }
            return nil
        }
        guard !splits.isEmpty else {
            return nil
        }
        
        var params: [String: String] = [:]
        splits.forEach{
            for (k,v) in $0 {
                params[k] = v
            }
        }
        return params
    }
}
